首先来回忆几个概念:
- 任务:需要执行的操作;
- 队列:储存任务的数据结构;
- 同步:只能开启一个子线程执行队列;
- 异步:可开启多个子线程执行队列;
- 串行:同一时间只能执行一个任务;
- 并行:同一时间可执行多个任务;
假设你已经了解上面几个概念,那么我们开始通过几个案例来了解一下死锁:
案例一
|
|
打印结果:
|
|
分析:
dispatch_get_main_queue
表示运行在主线程的主队列;dispatch_sync
表示是一个同步线程;2
是同步线程的主队列的任务;
首先执行1
,是没有问题的,只是接下来,程序遇到了同步线程 dispatch_sync
,那么它会进入队列,等待2
执行完,然后执行3
。但这是队列,有任务来,当然会将任务加到队尾部,然后遵循先入先出的原则执行任务,那么,2
就会被加到最后,3
排在 2
的前面。
使用 dispatch_sync
执行 mainQueue
时, mainQueue
存放着两个任务 1、3
,使用 block
加入了 2
以后,mainQueue
变成 1、3、2
,但在执行上,是 1、2、3
,所以执行队列到 2
时,2
需要等 3
先执行完,但是因为 dispatch_sync
是同步函数,不执行完 2
,是不会执行 3
的,所以 2
和 3
就进入了互相等的状态,死锁发生。
图解:
案例二
|
|
打印结果:
|
|
分析:
dispatch_get_global_queue
表示是一个全局并行队列;dispatch_sync
表示是一个同步线程;2
是同步线程的全局并行队列的任务;
首先执行 1
,接下来遇到一个同步线程,程序会进入等待,等待任务 2
完成以后,才会继续执行任务 3
,从 dispatch_get_global_queue
可以看出,2
被加入一个全局并行队列中,当 2
执行以后,不等待结果,返回到主队列,继续执行 3
。
图解:
案例三
|
|
打印结果:
|
|
分析:
这个案例没有使用系统提供的串行或并行队列,而是自己通过 dispatch_queue_creat
创建了DISPATCH_QUEUE_SERIAL
的串行队列。
- 执行
1
; - 遇到异步线程,将
2、同步线程、4
加入串行队列,因为是异步线程,所以主线程中的5
不必等待异步线程内的任务执行完毕; - 因为
5
不需要等待,所以异步线程里的2
,和主线程中的5
,执行顺序不能确定; 2
执行完以后,遇到同步线程,这时将3
加入串行队列;- 又因为
4
比3
先加入串行队列,所以4
执行完以后3
才会执行,但是同步线程
要执行完3
才能返回,所以造成死锁。
图解:
案例四
|
|
打印结果:
|
|
分析:
首先,主线程的队列是 mainQueue
,任务是:1、异步线程、5
;
异步线程的队列是 globalQueue
,任务是:2、同步线程、4
- 主线程中执行
1
,然后遇到异步函数 ,将2、同步线程、4
加入并行队列; - 开线程,因为
2
在异步线程,所以2
和5
的执行顺序随机,但在同一级运行; - 异步线程中遇到同步线程,执行主队列,将
3
加入到主队列,这时3 在 5的后面
; - 这时
5
,已经执行完,所以3
可以执行,然后执行4
。
从以上分析看,2
和 5
的输出顺序不确定,但是 5
一定在 3
前面,3
一定在 4
前面;
图解:
案例五
|
|
打印结果:
|
|
分析:
首先来看下,加进了哪些任务:
mainQueue
里面有 异步线程、4、死循环、5
,globalQueue
里面有 1、同步线程、3
。
- 异步线程,所以
4
不用等,1
和4
的顺序随机; - 主线程遇到死循环,所以
5
不会执行,异步线程中遇到同步线程执行主队列,2
在5
的后面,因为5
不会执行,所以2
是死锁,所以3
也不会执行。
最终结果是 1
和 4
,执行顺序随机。
图解: